/*
Author: Oliver A. Nina
email: onina@eecs.ucf.edu
Copyrights: This code is open source. You can use the code for personal or comercial purposes. 
Disclaimer: Code is AS IS. There is no warranty on using this code. Use this code at your own discretion.
If you use this code please cite the paper at : Interactive Enhancement on Handwritten Text trough Multi-resolution Gaussian, ICFHR, Bari, Italy 2012


This code is not perfect and has several issues. However, it gives a good example of the type of enhancement that can be done for handwritten text when using the scale space.
Please feel free to improve the code and you can send me the changes to make a better tool. 
Enjoy

*/




#include "stdafx.h"
#include "cv.h"
#include "cxcore.h"
#include "highgui.h"
#include "TextEnhancer.h"

#include <iostream>
using namespace  std;

TextEnhancer *text_enhancer ;
IplImage * image;


enum State{ IDLE,SMOOTH,DRAW_BACKGROUND};

State state= IDLE;

void display_image(char * win_name , IplImage* image){
	cvNamedWindow(win_name,0);
	cvShowImage(win_name,image);
}

bool is_coordinate_inside(int col,int row,IplImage* image){
	if(row >0 && col>0 && row<image->height && col< image->width)
		return true;
	else return false;
}

void display_rois(int x, int y){
	
	int stroke_width = text_enhancer->stroke_width;
	int i = y-stroke_width;
	int j = x-stroke_width;

	if(is_coordinate_inside(j,i,image)){
		
		cvSetImageROI(text_enhancer->roi, cvRect(j,i,2*stroke_width, 2*stroke_width));
		//display_image("roi",text_enhancer->roi);
		IplImage *roi_image = cvCreateImage(cvGetSize(text_enhancer->roi), text_enhancer->roi->depth, text_enhancer->roi->nChannels);
		cvCopy(text_enhancer->roi, roi_image, NULL);							
		cvResetImageROI(text_enhancer->roi);
		//display_image("roismoothed",text_enhancer->sroi);			
		IplImage *dog_image = cvCreateImage(cvGetSize(text_enhancer->sroi), text_enhancer->sroi->depth, text_enhancer->sroi->nChannels);
		cvSub(text_enhancer->sroi,roi_image,dog_image);
		//display_image("dog",dog_image);

	}
}

void enhance_image(IplImage* dog_image,int x,int y,int stroke_width){

	int cursor_width = text_enhancer->cursor_width; 

	float sum  = 0;
	int num_pixels = 0;
	for(int i=y-stroke_width, m =0; i<y+stroke_width;i++,m++)
	{
		for(int j=x-cursor_width, n=stroke_width - cursor_width; j<x+cursor_width;j++,n++)
		{
	
			if(is_coordinate_inside(j,i,image) && is_coordinate_inside(n,m,dog_image))
			{

				int pixel = CV_IMAGE_ELEM(text_enhancer->image,uchar,i,j);
				sum = sum + pixel;
				num_pixels++;
			}

		}
	}

	float mean = sum/num_pixels;

	int sum_var  = 0;
	for(int i=y-stroke_width, m =0; i<y+stroke_width;i++,m++)
	{
		for(int j=x-cursor_width, n=stroke_width - cursor_width; j<x+cursor_width;j++,n++)
		{
	
			if(is_coordinate_inside(j,i,image) && is_coordinate_inside(n,m,dog_image))
			{
				

				float diff = mean-CV_IMAGE_ELEM(text_enhancer->image,uchar,i,j);
				sum_var = sum_var + pow(diff,2);

			}

		}
	}
	 
	float var = sum_var/num_pixels;
	float std = sqrt(var);

	for(int i=y-stroke_width, m =0; i<y+stroke_width;i++,m++){
		for(int j=x-cursor_width, n=stroke_width - cursor_width; j<x+cursor_width;j++,n++){
	
			if(is_coordinate_inside(j,i,image) && is_coordinate_inside(n,m,dog_image))
			{
				
				CvScalar opixel = cvGet2D(text_enhancer->image,i,j);
				CvScalar dog_pixel = cvGet2D(dog_image,m,n);
				CvScalar rpixel = cvGet2D(text_enhancer->rimage,i,j);

				float pixel_map = CV_IMAGE_ELEM(text_enhancer->map,float,i,j);
				
				int dog_pixel_val = dog_pixel.val[0] * std/((255-mean)/pixel_map) ;
				//printf("\ndog_pixel: %d ",dog_pixel_val);
				int opixel_val = opixel.val[0];
				//printf("\nopixel_val: %d ",opixel_val);
				int new_value = opixel_val - dog_pixel_val ;
				//printf("\nnew_pixel: %d \n",new_value);


				if(new_value < 0)
					new_value = 0;

					if(new_value < rpixel.val[0])
					{
	
						if(new_value >= 0)
							CV_IMAGE_ELEM(text_enhancer->rimage,uchar,i,j)= new_value;
						else
							CV_IMAGE_ELEM(text_enhancer->rimage,uchar,i,j)= 0;

					}

			}

		}
	}
	
}

IplImage* get_enhancement_map(int x, int y, int stroke_width){

	//display_image("debug",text_enhancer->dimage);
	//display_image("map",text_enhancer->map);		
	cvSetImageROI(text_enhancer->roi, cvRect(x-stroke_width, y-stroke_width,2*stroke_width, 2*stroke_width));		
	IplImage *roi_image = cvCreateImage(cvGetSize(text_enhancer->roi), text_enhancer->roi->depth, text_enhancer->roi->nChannels);
	cvCopy(text_enhancer->roi, roi_image, NULL);		
	cvResetImageROI(text_enhancer->roi);

	cvSetImageROI(text_enhancer->sroi, cvRect(x-stroke_width, y-stroke_width,2*stroke_width, 2*stroke_width));		
	IplImage *roi_smoothed_image = cvCreateImage(cvGetSize(text_enhancer->sroi), text_enhancer->sroi->depth, text_enhancer->sroi->nChannels);
	cvCopy(text_enhancer->sroi, roi_smoothed_image, NULL);		

	IplImage *dog_image = cvCreateImage(cvGetSize(text_enhancer->sroi), text_enhancer->sroi->depth, text_enhancer->sroi->nChannels);
	cvSub(roi_smoothed_image,roi_image,dog_image);
	return dog_image;
}

void update_kernel_size_map(int x, int y, int stroke_width){
	for(int i=y-stroke_width; i<y+stroke_width;i++){
		if(i>0 && i<text_enhancer->image->height){

			/// for debugging purposes///
			CvScalar blueColor = CV_RGB(0,0,255);
			CV_IMAGE_ELEM(text_enhancer->dimage,uchar,i,x) = 255;
			float pixel_map = CV_IMAGE_ELEM(text_enhancer->map,float,i,x);
			float new_value = pixel_map + text_enhancer->kernel_size_increment;
			CV_IMAGE_ELEM(text_enhancer->map,float,i,x) = new_value;

		}
	}
}

void on_mouse( int event, int x, int y, int flags, void* param)
{
	
	CvPoint point= cvPoint(x, y);
	CvScalar forecolor = CV_RGB(255,255,255);
	CvScalar blueColor = CV_RGB(0,0,255);

	bool reset = false;

	switch( event )
	{
		case CV_EVENT_LBUTTONDBLCLK:
		{
			printf("left button click");
		}
		break;

		case CV_EVENT_MOUSEMOVE:
		{

			if(state == SMOOTH){
				
				if(is_coordinate_inside(x,y,text_enhancer->image))
				{

					int stroke_width = text_enhancer->stroke_width;;

					int kernel_width = CV_IMAGE_ELEM(text_enhancer->map,float,y,x);
				
					if(kernel_width%2 == 0) kernel_width = kernel_width +1;
					//printf("\nkernel_width: %d",kernel_width);

					text_enhancer->sroi = cvCreateImage(cvGetSize(text_enhancer->image), text_enhancer->image->depth, text_enhancer->image->nChannels);								
					cvSmooth( text_enhancer->image, text_enhancer->sroi,CV_GAUSSIAN,kernel_width,kernel_width );
					update_kernel_size_map(x,y,stroke_width);
					IplImage* dog_image = get_enhancement_map(x,y,stroke_width);
					enhance_image(dog_image,x,y,stroke_width);
					display_image("original",text_enhancer->rimage);


					
				}


			}

		}
		break;

		case CV_EVENT_LBUTTONDOWN:
		{
			state = SMOOTH;
			display_image("original",text_enhancer->rimage);	
			cvSet2D(text_enhancer->dimage,y,x,blueColor);
			//display_image("debug",text_enhancer->dimage);
			//display_image("map",text_enhancer->map);
			//display_rois(x,y);


		}
		break;

		case CV_EVENT_LBUTTONUP:
			{
			state = IDLE;
			text_enhancer->dimage = cvCloneImage(text_enhancer->image);
			//display_image("debug",text_enhancer->dimage);

			display_rois(x,y);
			}
			break;
		case CV_EVENT_RBUTTONUP:
			state = IDLE;
			break;
	
		case CV_EVENT_RBUTTONDOWN:
			break;

		case CV_EVENT_MBUTTONDOWN:
		{			
			cvSaveImage("results\\enhanced.png",text_enhancer->rimage);
		}
			break;

	}    
}

char * wchar_to_string(_TCHAR* widechar)
{
	int size=0;
	while( (char)widechar[size] != '\0'){
		size++;
	}
	size++;
	char * charpointer = new char[size];
	wcstombs(charpointer, widechar, size );
	return charpointer;
}

void run_as_shell_command(int argc, _TCHAR* argv[]){

	if(argc > 1){
		char * filename = wchar_to_string(argv[1]);
		printf(filename);
			
		image = cvLoadImage(filename);
		display_image("original", image);
		int stroke_width = 5;
		int kernel_size_increment = 10; 
		int cursor_width =3;
		int enhancement_value = 20;
		text_enhancer = new TextEnhancer(image, stroke_width, kernel_size_increment, enhancement_value, cursor_width);

		cvSetMouseCallback( "original", on_mouse );
		cvWaitKey(0); 

	}
	else{
		printf("enter more arguments");
	}
}

void run_test_app(){

	char* filename = ".\\testCases\\test2.jpg";
	//char* filename = ".\\testCases\\9-cropped.jpg";

	image = cvLoadImage(filename,0);
	display_image("original", image);
	int stroke_width = 5; // Parameter for the approximated width of the text in the image. If the enhancement is not noticeable you might need to increase this a little bit
	int kernel_size_increment = 10;  //Parameter for how much we want to increase the size of every scale 10
	int cursor_width =3;  // Parameter which indicates how long the cursor would be to process the pixels withing the cursor
	int enhancement_value = 20; // Parameter to increase the gain of the enhancement. bug: This parameter doesn't make much difference 
	text_enhancer = new TextEnhancer(image,stroke_width,kernel_size_increment, enhancement_value, cursor_width);
	cvSetMouseCallback( "original", on_mouse );
	cvWaitKey(0); 
}

int _tmain(int argc, _TCHAR* argv[])
{
	//run_as_shell_command( argc, argv);
	run_test_app();


}
